// OGoMojo
// Whitemice.MOGI.MOGIMon.Auditor
// License: LGPL
// Copyright: Whitemice Consulting, 2006
// Author(s): Adam Tauno Williams (awilliam@whitemice.org)
using System;
using Npgsql;
using System.Data;
using System.Collections;
using Whitemice.MOGI;

namespace Whitemice.MOGI.MOGIMon
{
	/// \brief Collects changes from OpenGroupware's Database
	public class Auditor
	{
		IServer			server;
		RecordKeeper	keeper;
		IDbConnection	connection;
		
		/// \brief Constructor
		/// \param server
		/// \param keeper
		public Auditor(IServer server, RecordKeeper keeper)
		{
			server.Log.Debug("Auditor object created");
			this.server = server;
			if (keeper == null)
			{
				server.Log.Fatal("Auditor was given a NULL keeper reference");
				throw new Exception("Auditor was given a NULL keeper reference");
			}
			 else
				this.keeper = keeper;
		}
		
		/// \brief Return collection of changes since last processed change
		public Hashtable Changes
		{
			get
			{
				return GetChangesSince(keeper.LastProcessedEntry);
			}
		}
		
		/// \brief Connect to OpenGroupware server
		/// \note Uses the Hostname, OGoDSNUID, and OGoDSNPWD values from the
		/// configuration file
		public void Connect()
		{
			string connectionString;
			
			connectionString =
				"Server=" + server.GetDirectiveValue("MOGIMon", "Hostname") + ";" +
				"Database=OGo;" +
				"User ID=" + server.GetDirectiveValue("MOGIMon", "OGoDSNUID") + ";" +
				"Password=" + server.GetDirectiveValue("MOGIMon", "OGoDSNPWD") + ";";
			connection = new NpgsqlConnection(connectionString);
			connection.Open();
		}
		
		public void Disconnect()
		{
			connection.Close();
			connection = null;
		}
		
		/// \brief Method called by Changes accessor
		/// \param auditEntry The audit id of the last processed event
		protected Hashtable GetChangesSince(int auditEntry)
		{
			IDbCommand	command;
			IDataReader	reader;
			Hashtable	changes;
			
			keeper.Log.Debug("Checking for changes since " + auditEntry.ToString());
			changes = new Hashtable();
			command = connection.CreateCommand();
			if (command == null)
			{
				keeper.Log.Fatal("Attempt to create data command failed");
				throw new Exception("Attempt to create data command failed");
			}
			command.CommandText = 
				"SELECT log_id, object_id, action " +
				"  FROM log " +
				" WHERE log_id > " + auditEntry.ToString() + 
				" ORDER BY log_id;";
			reader = command.ExecuteReader();
			if (reader == null)
			{
				keeper.Log.Fatal("DataReader is NULL after command execution.");
				throw new Exception("DataReader is NULL after command execution.");
			}
			while(reader.Read())
			{
				string 		documentName;
				int			auditId;
				ChangeType	change;
				
				keeper.Log.Debug("Processing audit entry " + reader.GetInt32(0).ToString());
				documentName = DetermineDocumentName(reader.GetInt32(1));
				if (!(documentName == null))
				{
					auditId = reader.GetInt32(0);
					change = DetermineChangeType(reader.GetString(2));				
					if (changes.ContainsKey(documentName))
					{
						switch(change)
						{
							case ChangeType.Delete:
								((Change)changes[documentName]).AuditEntry = auditId;
								((Change)changes[documentName]).ChangeType = change;
								break;
							case ChangeType.Update:
						    ((Change)changes[documentName]).AuditEntry = auditId;
								break;
						}
					}
					else changes.Add(
						documentName,
						new Change(auditId, documentName, change));
				} else
					{
						keeper.Log.Debug("Name of referenced entity is null, skipping change.");
					}
			} // End while(reader.Read())
			reader.Close();
      reader = null;
      command.Dispose();
      command = null;
			return changes;
		}
		
		/// \brief Look at audit string and determine type of event
		/// \param change String from audit log
		protected ChangeType DetermineChangeType(string change)
		{
			switch (change.Trim())
			{
				case "delete":
					return Whitemice.MOGI.MOGIMon.ChangeType.Delete;
				case "00_created":
					return Whitemice.MOGI.MOGIMon.ChangeType.Create;
				default:
					return Whitemice.MOGI.MOGIMon.ChangeType.Update;
			}
		}
		
		/// \brief Make a valid MOGI document name
		/// \param object_type Object type string from audit log
		/// \param object_id Primary key of object
		protected string DetermineDocumentName(int objectId)
		{
			return keeper.EntityName(objectId);
		}
	}
}